home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkEntry.c < prev    next >
C/C++ Source or Header  |  1995-05-28  |  68KB  |  2,172 lines

  1. /* 
  2.  * tkEntry.c --
  3.  *
  4.  *    This module implements entry widgets for the Tk
  5.  *    toolkit.  An entry displays a string and allows
  6.  *    the string to be edited.
  7.  *
  8.  * Copyright (c) 1990-1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  */
  14.  
  15. static char sccsid[] = "@(#) tkEntry.c 1.90 95/05/28 17:49:57";
  16.  
  17. #include "default.h"
  18. #include "tkPort.h"
  19. #include "tkInt.h"
  20.  
  21. /*
  22.  * A data structure of the following type is kept for each entry
  23.  * widget managed by this file:
  24.  */
  25.  
  26. typedef struct {
  27.     Tk_Window tkwin;        /* Window that embodies the entry. NULL
  28.                  * means that the window has been destroyed
  29.                  * but the data structures haven't yet been
  30.                  * cleaned up.*/
  31.     Display *display;        /* Display containing widget.  Used, among
  32.                  * other things, so that resources can be
  33.                  * freed even after tkwin has gone away. */
  34.     Tcl_Interp *interp;        /* Interpreter associated with entry. */
  35.     Tcl_Command widgetCmd;    /* Token for entry's widget command. */
  36.     int numChars;        /* Number of non-NULL characters in
  37.                  * string (may be 0). */
  38.     char *string;        /* Pointer to storage for string;
  39.                  * NULL-terminated;  malloc-ed. */
  40.     char *textVarName;        /* Name of variable (malloc'ed) or NULL.
  41.                  * If non-NULL, entry's string tracks the
  42.                  * contents of this variable and vice versa. */
  43.     Tk_Uid state;        /* Normal or disabled.  Entry is read-only
  44.                  * when disabled. */
  45.  
  46.     /*
  47.      * Information used when displaying widget:
  48.      */
  49.  
  50.     Tk_3DBorder normalBorder;    /* Used for drawing border around whole
  51.                  * window, plus used for background. */
  52.     int borderWidth;        /* Width of 3-D border around window. */
  53.     int relief;            /* 3-D effect: TK_RELIEF_RAISED, etc. */
  54.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  55.     XColor *fgColorPtr;        /* Text color in normal mode. */
  56.     GC textGC;            /* For drawing normal text. */
  57.     Tk_3DBorder selBorder;    /* Border and background for selected
  58.                  * characters. */
  59.     int selBorderWidth;        /* Width of border around selection. */
  60.     XColor *selFgColorPtr;    /* Foreground color for selected text. */
  61.     GC selTextGC;        /* For drawing selected text. */
  62.     Tk_3DBorder insertBorder;    /* Used to draw vertical bar for insertion
  63.                  * cursor. */
  64.     int insertWidth;        /* Total width of insert cursor. */
  65.     int insertBorderWidth;    /* Width of 3-D border around insert cursor. */
  66.     int insertOnTime;        /* Number of milliseconds cursor should spend
  67.                  * in "on" state for each blink. */
  68.     int insertOffTime;        /* Number of milliseconds cursor should spend
  69.                  * in "off" state for each blink. */
  70.     Tk_TimerToken insertBlinkHandler;
  71.                 /* Timer handler used to blink cursor on and
  72.                  * off. */
  73.     int highlightWidth;        /* Width in pixels of highlight to draw
  74.                  * around widget when it has the focus.
  75.                  * <= 0 means don't draw a highlight. */
  76.     XColor *highlightBgColorPtr;
  77.                 /* Color for drawing traversal highlight
  78.                  * area when highlight is off. */
  79.     XColor *highlightColorPtr;    /* Color for drawing traversal highlight. */
  80.     GC highlightGC;        /* For drawing traversal highlight. */
  81.     Tk_Justify justify;        /* Justification to use for text within
  82.                  * window. */
  83.     int avgWidth;        /* Width of average character. */
  84.     int prefWidth;        /* Desired width of window, measured in
  85.                  * average characters. */
  86.     int inset;            /* Number of pixels on the left and right
  87.                  * sides that are taken up by XPAD, borderWidth
  88.                  * (if any), and highlightWidth (if any). */
  89.     int leftIndex;        /* Index of left-most character visible in
  90.                  * window. */
  91.     int leftX;            /* X position at which leftIndex is drawn
  92.                  * (varies depending on justify). */
  93.     int tabOrigin;        /* Origin for tabs (left edge of string[0]). */
  94.     int insertPos;        /* Index of character before which next
  95.                  * typed character will be inserted. */
  96.     char *showChar;        /* Value of -show option.  If non-NULL, first
  97.                  * character is used for displaying all
  98.                  * characters in entry.  Malloc'ed. */
  99.     char *displayString;    /* If non-NULL, points to string with same
  100.                  * length as string but whose characters
  101.                  * are all equal to showChar.  Malloc'ed. */
  102.  
  103.     /*
  104.      * Information about what's selected, if any.
  105.      */
  106.  
  107.     int selectFirst;        /* Index of first selected character (-1 means
  108.                  * nothing selected. */
  109.     int selectLast;        /* Index of last selected character (-1 means
  110.                  * nothing selected. */
  111.     int selectAnchor;        /* Fixed end of selection (i.e. "select to"
  112.                  * operation will use this as one end of the
  113.                  * selection). */
  114.     int exportSelection;    /* Non-zero means tie internal entry selection
  115.                  * to X selection. */
  116.  
  117.     /*
  118.      * Information for scanning:
  119.      */
  120.  
  121.     int scanMarkX;        /* X-position at which scan started (e.g.
  122.                  * button was pressed here). */
  123.     int scanMarkIndex;        /* Index of character that was at left of
  124.                  * window when scan started. */
  125.  
  126.     /*
  127.      * Miscellaneous information:
  128.      */
  129.  
  130.     Cursor cursor;        /* Current cursor for window, or None. */
  131.     char *takeFocus;        /* Value of -takefocus option;  not used in
  132.                  * the C code, but used by keyboard traversal
  133.                  * scripts.  Malloc'ed, but may be NULL. */
  134.     char *scrollCmd;        /* Command prefix for communicating with
  135.                  * scrollbar(s).  Malloc'ed.  NULL means
  136.                  * no command to issue. */
  137.     int flags;            /* Miscellaneous flags;  see below for
  138.                  * definitions. */
  139. } Entry;
  140.  
  141. /*
  142.  * Assigned bits of "flags" fields of Entry structures, and what those
  143.  * bits mean:
  144.  *
  145.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler has
  146.  *                already been queued to redisplay the entry.
  147.  * BORDER_NEEDED:        Non-zero means 3-D border must be redrawn
  148.  *                around window during redisplay.  Normally
  149.  *                only text portion needs to be redrawn.
  150.  * CURSOR_ON:            Non-zero means insert cursor is displayed at
  151.  *                present.  0 means it isn't displayed.
  152.  * GOT_FOCUS:            Non-zero means this window has the input
  153.  *                focus.
  154.  * UPDATE_SCROLLBAR:        Non-zero means scrollbar should be updated
  155.  *                during next redisplay operation.
  156.  */
  157.  
  158. #define REDRAW_PENDING        1
  159. #define BORDER_NEEDED        2
  160. #define CURSOR_ON        4
  161. #define GOT_FOCUS        8
  162. #define UPDATE_SCROLLBAR    16
  163.  
  164. /*
  165.  * The following macro defines how many extra pixels to leave on each
  166.  * side of the text in the entry.
  167.  */
  168.  
  169. #define XPAD 1
  170. #define YPAD 1
  171.  
  172. /*
  173.  * Information used for argv parsing.
  174.  */
  175.  
  176. static Tk_ConfigSpec configSpecs[] = {
  177.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  178.     DEF_ENTRY_BG_COLOR, Tk_Offset(Entry, normalBorder),
  179.     TK_CONFIG_COLOR_ONLY},
  180.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  181.     DEF_ENTRY_BG_MONO, Tk_Offset(Entry, normalBorder),
  182.     TK_CONFIG_MONO_ONLY},
  183.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  184.     (char *) NULL, 0, 0},
  185.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  186.     (char *) NULL, 0, 0},
  187.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  188.     DEF_ENTRY_BORDER_WIDTH, Tk_Offset(Entry, borderWidth), 0},
  189.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  190.     DEF_ENTRY_CURSOR, Tk_Offset(Entry, cursor), TK_CONFIG_NULL_OK},
  191.     {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
  192.     "ExportSelection", DEF_ENTRY_EXPORT_SELECTION,
  193.     Tk_Offset(Entry, exportSelection), 0},
  194.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  195.     (char *) NULL, 0, 0},
  196.     {TK_CONFIG_FONT, "-font", "font", "Font",
  197.     DEF_ENTRY_FONT, Tk_Offset(Entry, fontPtr), 0},
  198.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  199.     DEF_ENTRY_FG, Tk_Offset(Entry, fgColorPtr), 0},
  200.     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
  201.     "HighlightBackground", DEF_ENTRY_HIGHLIGHT_BG,
  202.     Tk_Offset(Entry, highlightBgColorPtr), 0},
  203.     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  204.     DEF_ENTRY_HIGHLIGHT, Tk_Offset(Entry, highlightColorPtr), 0},
  205.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  206.     "HighlightThickness",
  207.     DEF_ENTRY_HIGHLIGHT_WIDTH, Tk_Offset(Entry, highlightWidth), 0},
  208.     {TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
  209.     DEF_ENTRY_INSERT_BG, Tk_Offset(Entry, insertBorder), 0},
  210.     {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
  211.     DEF_ENTRY_INSERT_BD_COLOR, Tk_Offset(Entry, insertBorderWidth),
  212.     TK_CONFIG_COLOR_ONLY},
  213.     {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
  214.     DEF_ENTRY_INSERT_BD_MONO, Tk_Offset(Entry, insertBorderWidth),
  215.     TK_CONFIG_MONO_ONLY},
  216.     {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime",
  217.     DEF_ENTRY_INSERT_OFF_TIME, Tk_Offset(Entry, insertOffTime), 0},
  218.     {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime",
  219.     DEF_ENTRY_INSERT_ON_TIME, Tk_Offset(Entry, insertOnTime), 0},
  220.     {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
  221.     DEF_ENTRY_INSERT_WIDTH, Tk_Offset(Entry, insertWidth), 0},
  222.     {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
  223.     DEF_ENTRY_JUSTIFY, Tk_Offset(Entry, justify), 0},
  224.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  225.     DEF_ENTRY_RELIEF, Tk_Offset(Entry, relief), 0},
  226.     {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
  227.     DEF_ENTRY_SELECT_COLOR, Tk_Offset(Entry, selBorder),
  228.     TK_CONFIG_COLOR_ONLY},
  229.     {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
  230.     DEF_ENTRY_SELECT_MONO, Tk_Offset(Entry, selBorder),
  231.     TK_CONFIG_MONO_ONLY},
  232.     {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
  233.     DEF_ENTRY_SELECT_BD_COLOR, Tk_Offset(Entry, selBorderWidth),
  234.     TK_CONFIG_COLOR_ONLY},
  235.     {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
  236.     DEF_ENTRY_SELECT_BD_MONO, Tk_Offset(Entry, selBorderWidth),
  237.     TK_CONFIG_MONO_ONLY},
  238.     {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
  239.     DEF_ENTRY_SELECT_FG_COLOR, Tk_Offset(Entry, selFgColorPtr),
  240.     TK_CONFIG_COLOR_ONLY},
  241.     {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
  242.     DEF_ENTRY_SELECT_FG_MONO, Tk_Offset(Entry, selFgColorPtr),
  243.     TK_CONFIG_MONO_ONLY},
  244.     {TK_CONFIG_STRING, "-show", "show", "Show",
  245.     DEF_ENTRY_SHOW, Tk_Offset(Entry, showChar), TK_CONFIG_NULL_OK},
  246.     {TK_CONFIG_UID, "-state", "state", "State",
  247.     DEF_ENTRY_STATE, Tk_Offset(Entry, state), 0},
  248.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  249.     DEF_ENTRY_TAKE_FOCUS, Tk_Offset(Entry, takeFocus), TK_CONFIG_NULL_OK},
  250.     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
  251.     DEF_ENTRY_TEXT_VARIABLE, Tk_Offset(Entry, textVarName),
  252.     TK_CONFIG_NULL_OK},
  253.     {TK_CONFIG_INT, "-width", "width", "Width",
  254.     DEF_ENTRY_WIDTH, Tk_Offset(Entry, prefWidth), 0},
  255.     {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
  256.     DEF_ENTRY_SCROLL_COMMAND, Tk_Offset(Entry, scrollCmd),
  257.     TK_CONFIG_NULL_OK},
  258.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  259.     (char *) NULL, 0, 0}
  260. };
  261.  
  262. /*
  263.  * Flags for GetEntryIndex procedure:
  264.  */
  265.  
  266. #define ZERO_OK            1
  267. #define LAST_PLUS_ONE_OK    2
  268.  
  269. /*
  270.  * Forward declarations for procedures defined later in this file:
  271.  */
  272.  
  273. static int        ConfigureEntry _ANSI_ARGS_((Tcl_Interp *interp,
  274.                 Entry *entryPtr, int argc, char **argv,
  275.                 int flags));
  276. static void        DeleteChars _ANSI_ARGS_((Entry *entryPtr, int index,
  277.                 int count));
  278. static void        DestroyEntry _ANSI_ARGS_((ClientData clientData));
  279. static void        DisplayEntry _ANSI_ARGS_((ClientData clientData));
  280. static void        EntryBlinkProc _ANSI_ARGS_((ClientData clientData));
  281. static void        EntryCmdDeletedProc _ANSI_ARGS_((
  282.                 ClientData clientData));
  283. static void        EntryComputeGeometry _ANSI_ARGS_((Entry *entryPtr));
  284. static void        EntryEventProc _ANSI_ARGS_((ClientData clientData,
  285.                 XEvent *eventPtr));
  286. static void        EntryFocusProc _ANSI_ARGS_ ((Entry *entryPtr,
  287.                 int gotFocus));
  288. static int        EntryFetchSelection _ANSI_ARGS_((ClientData clientData,
  289.                 int offset, char *buffer, int maxBytes));
  290. static void        EntryLostSelection _ANSI_ARGS_((
  291.                 ClientData clientData));
  292. static void        EventuallyRedraw _ANSI_ARGS_((Entry *entryPtr));
  293. static void        EntryScanTo _ANSI_ARGS_((Entry *entryPtr, int y));
  294. static void        EntrySetValue _ANSI_ARGS_((Entry *entryPtr,
  295.                 char *value));
  296. static void        EntrySelectTo _ANSI_ARGS_((
  297.                 Entry *entryPtr, int index));
  298. static char *        EntryTextVarProc _ANSI_ARGS_((ClientData clientData,
  299.                 Tcl_Interp *interp, char *name1, char *name2,
  300.                 int flags));
  301. static void        EntryUpdateScrollbar _ANSI_ARGS_((Entry *entryPtr));
  302. static void        EntryVisibleRange _ANSI_ARGS_((Entry *entryPtr,
  303.                 double *firstPtr, double *lastPtr));
  304. static int        EntryWidgetCmd _ANSI_ARGS_((ClientData clientData,
  305.                 Tcl_Interp *interp, int argc, char **argv));
  306. static int        GetEntryIndex _ANSI_ARGS_((Tcl_Interp *interp,
  307.                 Entry *entryPtr, char *string, int *indexPtr));
  308. static void        InsertChars _ANSI_ARGS_((Entry *entryPtr, int index,
  309.                 char *string));
  310.  
  311. /*
  312.  *--------------------------------------------------------------
  313.  *
  314.  * Tk_EntryCmd --
  315.  *
  316.  *    This procedure is invoked to process the "entry" Tcl
  317.  *    command.  See the user documentation for details on what
  318.  *    it does.
  319.  *
  320.  * Results:
  321.  *    A standard Tcl result.
  322.  *
  323.  * Side effects:
  324.  *    See the user documentation.
  325.  *
  326.  *--------------------------------------------------------------
  327.  */
  328.  
  329. int
  330. Tk_EntryCmd(clientData, interp, argc, argv)
  331.     ClientData clientData;    /* Main window associated with
  332.                  * interpreter. */
  333.     Tcl_Interp *interp;        /* Current interpreter. */
  334.     int argc;            /* Number of arguments. */
  335.     char **argv;        /* Argument strings. */
  336. {
  337.     Tk_Window tkwin = (Tk_Window) clientData;
  338.     register Entry *entryPtr;
  339.     Tk_Window new;
  340.  
  341.     if (argc < 2) {
  342.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  343.         argv[0], " pathName ?options?\"", (char *) NULL);
  344.     return TCL_ERROR;
  345.     }
  346.  
  347.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  348.     if (new == NULL) {
  349.     return TCL_ERROR;
  350.     }
  351.  
  352.     /*
  353.      * Initialize the fields of the structure that won't be initialized
  354.      * by ConfigureEntry, or that ConfigureEntry requires to be
  355.      * initialized already (e.g. resource pointers).
  356.      */
  357.  
  358.     entryPtr = (Entry *) ckalloc(sizeof(Entry));
  359.     entryPtr->tkwin = new;
  360.     entryPtr->display = Tk_Display(new);
  361.     entryPtr->interp = interp;
  362.     entryPtr->widgetCmd = Tcl_CreateCommand(interp,
  363.         Tk_PathName(entryPtr->tkwin), EntryWidgetCmd,
  364.         (ClientData) entryPtr, EntryCmdDeletedProc);
  365.     entryPtr->numChars = 0;
  366.     entryPtr->string = (char *) ckalloc(1);
  367.     entryPtr->string[0] = '\0';
  368.     entryPtr->textVarName = NULL;
  369.     entryPtr->state = tkNormalUid;
  370.     entryPtr->normalBorder = NULL;
  371.     entryPtr->borderWidth = 0;
  372.     entryPtr->relief = TK_RELIEF_FLAT;
  373.     entryPtr->fontPtr = NULL;
  374.     entryPtr->fgColorPtr = NULL;
  375.     entryPtr->textGC = None;
  376.     entryPtr->selBorder = NULL;
  377.     entryPtr->selBorderWidth = 0;
  378.     entryPtr->selFgColorPtr = NULL;
  379.     entryPtr->selTextGC = None;
  380.     entryPtr->insertBorder = NULL;
  381.     entryPtr->insertWidth = 0;
  382.     entryPtr->insertBorderWidth = 0;
  383.     entryPtr->insertOnTime = 0;
  384.     entryPtr->insertOffTime = 0;
  385.     entryPtr->insertBlinkHandler = (Tk_TimerToken) NULL;
  386.     entryPtr->highlightWidth = 0;
  387.     entryPtr->highlightBgColorPtr = NULL;
  388.     entryPtr->highlightColorPtr = NULL;
  389.     entryPtr->justify = TK_JUSTIFY_LEFT;
  390.     entryPtr->avgWidth = 1;
  391.     entryPtr->prefWidth = 0;
  392.     entryPtr->inset = XPAD;
  393.     entryPtr->leftIndex = 0;
  394.     entryPtr->leftX = 0;
  395.     entryPtr->tabOrigin = 0;
  396.     entryPtr->insertPos = 0;
  397.     entryPtr->showChar = NULL;
  398.     entryPtr->displayString = NULL;
  399.     entryPtr->selectFirst = -1;
  400.     entryPtr->selectLast = -1;
  401.     entryPtr->selectAnchor = 0;
  402.     entryPtr->exportSelection = 1;
  403.     entryPtr->scanMarkX = 0;
  404.     entryPtr->scanMarkIndex = 0;
  405.     entryPtr->cursor = None;
  406.     entryPtr->takeFocus = NULL;
  407.     entryPtr->scrollCmd = NULL;
  408.     entryPtr->flags = 0;
  409.  
  410.     Tk_SetClass(entryPtr->tkwin, "Entry");
  411.     Tk_CreateEventHandler(entryPtr->tkwin,
  412.         ExposureMask|StructureNotifyMask|FocusChangeMask,
  413.         EntryEventProc, (ClientData) entryPtr);
  414.     Tk_CreateSelHandler(entryPtr->tkwin, XA_PRIMARY, XA_STRING,
  415.         EntryFetchSelection, (ClientData) entryPtr, XA_STRING);
  416.     if (ConfigureEntry(interp, entryPtr, argc-2, argv+2, 0) != TCL_OK) {
  417.     goto error;
  418.     }
  419.  
  420.     interp->result = Tk_PathName(entryPtr->tkwin);
  421.     return TCL_OK;
  422.  
  423.     error:
  424.     Tk_DestroyWindow(entryPtr->tkwin);
  425.     return TCL_ERROR;
  426. }
  427.  
  428. /*
  429.  *--------------------------------------------------------------
  430.  *
  431.  * EntryWidgetCmd --
  432.  *
  433.  *    This procedure is invoked to process the Tcl command
  434.  *    that corresponds to a widget managed by this module.
  435.  *    See the user documentation for details on what it does.
  436.  *
  437.  * Results:
  438.  *    A standard Tcl result.
  439.  *
  440.  * Side effects:
  441.  *    See the user documentation.
  442.  *
  443.  *--------------------------------------------------------------
  444.  */
  445.  
  446. static int
  447. EntryWidgetCmd(clientData, interp, argc, argv)
  448.     ClientData clientData;        /* Information about entry widget. */
  449.     Tcl_Interp *interp;            /* Current interpreter. */
  450.     int argc;                /* Number of arguments. */
  451.     char **argv;            /* Argument strings. */
  452. {
  453.     register Entry *entryPtr = (Entry *) clientData;
  454.     int result = TCL_OK;
  455.     size_t length;
  456.     int c;
  457.  
  458.     if (argc < 2) {
  459.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  460.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  461.     return TCL_ERROR;
  462.     }
  463.     Tk_Preserve((ClientData) entryPtr);
  464.     c = argv[1][0];
  465.     length = strlen(argv[1]);
  466.     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  467.         && (length >= 2)) {
  468.     if (argc != 3) {
  469.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  470.             argv[0], " cget option\"",
  471.             (char *) NULL);
  472.         goto error;
  473.     }
  474.     result = Tk_ConfigureValue(interp, entryPtr->tkwin, configSpecs,
  475.         (char *) entryPtr, argv[2], 0);
  476.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  477.         && (length >= 2)) {
  478.     if (argc == 2) {
  479.         result = Tk_ConfigureInfo(interp, entryPtr->tkwin, configSpecs,
  480.             (char *) entryPtr, (char *) NULL, 0);
  481.     } else if (argc == 3) {
  482.         result = Tk_ConfigureInfo(interp, entryPtr->tkwin, configSpecs,
  483.             (char *) entryPtr, argv[2], 0);
  484.     } else {
  485.         result = ConfigureEntry(interp, entryPtr, argc-2, argv+2,
  486.             TK_CONFIG_ARGV_ONLY);
  487.     }
  488.     } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
  489.     int first, last;
  490.  
  491.     if ((argc < 3) || (argc > 4)) {
  492.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  493.             argv[0], " delete firstIndex ?lastIndex?\"",
  494.             (char *) NULL);
  495.         goto error;
  496.     }
  497.     if (GetEntryIndex(interp, entryPtr, argv[2], &first) != TCL_OK) {
  498.         goto error;
  499.     }
  500.     if (argc == 3) {
  501.         last = first+1;
  502.     } else {
  503.         if (GetEntryIndex(interp, entryPtr, argv[3], &last) != TCL_OK) {
  504.         goto error;
  505.         }
  506.     }
  507.     if ((last >= first) && (entryPtr->state == tkNormalUid)) {
  508.         DeleteChars(entryPtr, first, last-first);
  509.     }
  510.     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
  511.     if (argc != 2) {
  512.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  513.             argv[0], " get\"", (char *) NULL);
  514.         goto error;
  515.     }
  516.     interp->result = entryPtr->string;
  517.     } else if ((c == 'i') && (strncmp(argv[1], "icursor", length) == 0)
  518.         && (length >= 2)) {
  519.     if (argc != 3) {
  520.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  521.             argv[0], " icursor pos\"",
  522.             (char *) NULL);
  523.         goto error;
  524.     }
  525.     if (GetEntryIndex(interp, entryPtr, argv[2], &entryPtr->insertPos)
  526.         != TCL_OK) {
  527.         goto error;
  528.     }
  529.     EventuallyRedraw(entryPtr);
  530.     } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
  531.         && (length >= 3)) {
  532.     int index;
  533.  
  534.     if (argc != 3) {
  535.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  536.             argv[0], " index string\"", (char *) NULL);
  537.         goto error;
  538.     }
  539.     if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
  540.         goto error;
  541.     }
  542.     sprintf(interp->result, "%d", index);
  543.     } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
  544.         && (length >= 3)) {
  545.     int index;
  546.  
  547.     if (argc != 4) {
  548.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  549.             argv[0], " insert index text\"",
  550.             (char *) NULL);
  551.         goto error;
  552.     }
  553.     if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
  554.         goto error;
  555.     }
  556.     if (entryPtr->state == tkNormalUid) {
  557.         InsertChars(entryPtr, index, argv[3]);
  558.     }
  559.     } else if ((c == 's') && (length >= 2)
  560.         && (strncmp(argv[1], "scan", length) == 0)) {
  561.     int x;
  562.  
  563.     if (argc != 4) {
  564.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  565.             argv[0], " scan mark|dragto x\"", (char *) NULL);
  566.         goto error;
  567.     }
  568.     if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
  569.         goto error;
  570.     }
  571.     if ((argv[2][0] == 'm')
  572.         && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
  573.         entryPtr->scanMarkX = x;
  574.         entryPtr->scanMarkIndex = entryPtr->leftIndex;
  575.     } else if ((argv[2][0] == 'd')
  576.         && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
  577.         EntryScanTo(entryPtr, x);
  578.     } else {
  579.         Tcl_AppendResult(interp, "bad scan option \"", argv[2],
  580.             "\":  must be mark or dragto", (char *) NULL);
  581.         goto error;
  582.     }
  583.     } else if ((c == 's') && (length >= 2)
  584.         && (strncmp(argv[1], "selection", length) == 0)) {
  585.     int index, index2;
  586.  
  587.     if (argc < 3) {
  588.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  589.             argv[0], " select option ?index?\"", (char *) NULL);
  590.         goto error;
  591.     }
  592.     length = strlen(argv[2]);
  593.     c = argv[2][0];
  594.     if ((c == 'c') && (strncmp(argv[2], "clear", length) == 0)) {
  595.         if (argc != 3) {
  596.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  597.             argv[0], " selection clear\"", (char *) NULL);
  598.         goto error;
  599.         }
  600.         if (entryPtr->selectFirst != -1) {
  601.         entryPtr->selectFirst = entryPtr->selectLast = -1;
  602.         EventuallyRedraw(entryPtr);
  603.         }
  604.         goto done;
  605.     } else if ((c == 'p') && (strncmp(argv[2], "present", length) == 0)) {
  606.         if (argc != 3) {
  607.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  608.             argv[0], " selection present\"", (char *) NULL);
  609.         goto error;
  610.         }
  611.         if (entryPtr->selectFirst == -1) {
  612.         interp->result = "0";
  613.         } else {
  614.         interp->result = "1";
  615.         }
  616.         goto done;
  617.     }
  618.     if (argc >= 4) {
  619.         if (GetEntryIndex(interp, entryPtr, argv[3], &index) != TCL_OK) {
  620.         goto error;
  621.         }
  622.     }
  623.     if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
  624.         if (argc != 4) {
  625.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  626.             argv[0], " selection adjust index\"",
  627.             (char *) NULL);
  628.         goto error;
  629.         }
  630.         if (entryPtr->selectFirst >= 0) {
  631.         int half1, half2;
  632.  
  633.         half1 = (entryPtr->selectFirst + entryPtr->selectLast)/2;
  634.         half2 = (entryPtr->selectFirst + entryPtr->selectLast + 1)/2;
  635.         if (index < half1) {
  636.             entryPtr->selectAnchor = entryPtr->selectLast;
  637.         } else if (index > half2) {
  638.             entryPtr->selectAnchor = entryPtr->selectFirst;
  639.         } else {
  640.             /*
  641.              * We're at about the halfway point in the selection;
  642.              * just keep the existing anchor.
  643.              */
  644.         }
  645.         }
  646.         EntrySelectTo(entryPtr, index);
  647.     } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
  648.         if (argc != 4) {
  649.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  650.             argv[0], " selection from index\"",
  651.             (char *) NULL);
  652.         goto error;
  653.         }
  654.         entryPtr->selectAnchor = index;
  655.     } else if ((c == 'r') && (strncmp(argv[2], "range", length) == 0)) {
  656.         if (argc != 5) {
  657.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  658.             argv[0], " selection range start end\"",
  659.             (char *) NULL);
  660.         goto error;
  661.         }
  662.         if (GetEntryIndex(interp, entryPtr, argv[4], &index2) != TCL_OK) {
  663.         goto error;
  664.         }
  665.         if (index >= index2) {
  666.         entryPtr->selectFirst = entryPtr->selectLast = -1;
  667.         } else {
  668.         if ((entryPtr->selectFirst == -1)
  669.             && (entryPtr->exportSelection)) {
  670.             Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, 
  671.                 EntryLostSelection, (ClientData) entryPtr);
  672.         }
  673.         entryPtr->selectFirst = index;
  674.         entryPtr->selectLast = index2;
  675.         }
  676.         if ((entryPtr->selectFirst == -1) && (entryPtr->exportSelection)) {
  677.         Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY,
  678.             EntryLostSelection, (ClientData) entryPtr);
  679.         }
  680.         EventuallyRedraw(entryPtr);
  681.     } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
  682.         if (argc != 4) {
  683.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  684.             argv[0], " selection to index\"",
  685.             (char *) NULL);
  686.         goto error;
  687.         }
  688.         EntrySelectTo(entryPtr, index);
  689.     } else {
  690.         Tcl_AppendResult(interp, "bad selection option \"", argv[2],
  691.             "\": must be adjust, clear, from, present, range, or to",
  692.             (char *) NULL);
  693.         goto error;
  694.     }
  695.     } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
  696.     int index, type, count, charsPerPage;
  697.     double fraction, first, last;
  698.  
  699.     if (argc == 2) {
  700.         EntryVisibleRange(entryPtr, &first, &last);
  701.         sprintf(interp->result, "%g %g", first, last);
  702.         goto done;
  703.     } else if (argc == 3) {
  704.         if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
  705.         goto error;
  706.         }
  707.     } else {
  708.         type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
  709.         index = entryPtr->leftIndex;
  710.         switch (type) {
  711.         case TK_SCROLL_ERROR:
  712.             goto error;
  713.         case TK_SCROLL_MOVETO:
  714.             index = (fraction * entryPtr->numChars);
  715.             break;
  716.         case TK_SCROLL_PAGES:
  717.             charsPerPage = ((Tk_Width(entryPtr->tkwin)
  718.                 - 2*entryPtr->inset) / entryPtr->avgWidth) - 2;
  719.             if (charsPerPage < 1) {
  720.             charsPerPage = 1;
  721.             }
  722.             index += charsPerPage*count;
  723.             break;
  724.         case TK_SCROLL_UNITS:
  725.             index += count;
  726.             break;
  727.         }
  728.     }
  729.     if (index >= entryPtr->numChars) {
  730.         index = entryPtr->numChars-1;
  731.     }
  732.     if (index < 0) {
  733.         index = 0;
  734.     }
  735.     entryPtr->leftIndex = index;
  736.     entryPtr->flags |= UPDATE_SCROLLBAR;
  737.     EntryComputeGeometry(entryPtr);
  738.     EventuallyRedraw(entryPtr);
  739.     } else {
  740.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  741.         "\": must be cget, configure, delete, get, ",
  742.         "icursor, index, insert, scan, selection, or xview",
  743.         (char *) NULL);
  744.     goto error;
  745.     }
  746.     done:
  747.     Tk_Release((ClientData) entryPtr);
  748.     return result;
  749.  
  750.     error:
  751.     Tk_Release((ClientData) entryPtr);
  752.     return TCL_ERROR;
  753. }
  754.  
  755. /*
  756.  *----------------------------------------------------------------------
  757.  *
  758.  * DestroyEntry --
  759.  *
  760.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  761.  *    to clean up the internal structure of an entry at a safe time
  762.  *    (when no-one is using it anymore).
  763.  *
  764.  * Results:
  765.  *    None.
  766.  *
  767.  * Side effects:
  768.  *    Everything associated with the entry is freed up.
  769.  *
  770.  *----------------------------------------------------------------------
  771.  */
  772.  
  773. static void
  774. DestroyEntry(clientData)
  775.     ClientData clientData;            /* Info about entry widget. */
  776. {
  777.     register Entry *entryPtr = (Entry *) clientData;
  778.  
  779.     /*
  780.      * Free up all the stuff that requires special handling, then
  781.      * let Tk_FreeOptions handle all the standard option-related
  782.      * stuff.
  783.      */
  784.  
  785.     ckfree(entryPtr->string);
  786.     if (entryPtr->textVarName != NULL) {
  787.     Tcl_UntraceVar(entryPtr->interp, entryPtr->textVarName,
  788.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  789.         EntryTextVarProc, (ClientData) entryPtr);
  790.     }
  791.     if (entryPtr->textGC != None) {
  792.     Tk_FreeGC(entryPtr->display, entryPtr->textGC);
  793.     }
  794.     if (entryPtr->selTextGC != None) {
  795.     Tk_FreeGC(entryPtr->display, entryPtr->selTextGC);
  796.     }
  797.     Tk_DeleteTimerHandler(entryPtr->insertBlinkHandler);
  798.     if (entryPtr->displayString != NULL) {
  799.     ckfree(entryPtr->displayString);
  800.     }
  801.     Tk_FreeOptions(configSpecs, (char *) entryPtr, entryPtr->display, 0);
  802.     ckfree((char *) entryPtr);
  803. }
  804.  
  805. /*
  806.  *----------------------------------------------------------------------
  807.  *
  808.  * ConfigureEntry --
  809.  *
  810.  *    This procedure is called to process an argv/argc list, plus
  811.  *    the Tk option database, in order to configure (or reconfigure)
  812.  *    an entry widget.
  813.  *
  814.  * Results:
  815.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  816.  *    returned, then interp->result contains an error message.
  817.  *
  818.  * Side effects:
  819.  *    Configuration information, such as colors, border width,
  820.  *    etc. get set for entryPtr;  old resources get freed,
  821.  *    if there were any.
  822.  *
  823.  *----------------------------------------------------------------------
  824.  */
  825.  
  826. static int
  827. ConfigureEntry(interp, entryPtr, argc, argv, flags)
  828.     Tcl_Interp *interp;        /* Used for error reporting. */
  829.     register Entry *entryPtr;    /* Information about widget;  may or may
  830.                  * not already have values for some fields. */
  831.     int argc;            /* Number of valid entries in argv. */
  832.     char **argv;        /* Arguments. */
  833.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  834. {
  835.     XGCValues gcValues;
  836.     GC new;
  837.     int oldExport;
  838.  
  839.     /*
  840.      * Eliminate any existing trace on a variable monitored by the entry.
  841.      */
  842.  
  843.     if (entryPtr->textVarName != NULL) {
  844.     Tcl_UntraceVar(interp, entryPtr->textVarName, 
  845.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  846.         EntryTextVarProc, (ClientData) entryPtr);
  847.     }
  848.  
  849.     oldExport = entryPtr->exportSelection;
  850.     if (Tk_ConfigureWidget(interp, entryPtr->tkwin, configSpecs,
  851.         argc, argv, (char *) entryPtr, flags) != TCL_OK) {
  852.     return TCL_ERROR;
  853.     }
  854.  
  855.     /*
  856.      * If the entry is tied to the value of a variable, then set up
  857.      * a trace on the variable's value, create the variable if it doesn't
  858.      * exist, and set the entry's value from the variable's value.
  859.      */
  860.  
  861.     if (entryPtr->textVarName != NULL) {
  862.     char *value;
  863.  
  864.     value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
  865.     if (value == NULL) {
  866.         Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
  867.             TCL_GLOBAL_ONLY);
  868.     } else {
  869.         EntrySetValue(entryPtr, value);
  870.     }
  871.     Tcl_TraceVar(interp, entryPtr->textVarName,
  872.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  873.         EntryTextVarProc, (ClientData) entryPtr);
  874.     }
  875.  
  876.     /*
  877.      * A few other options also need special processing, such as parsing
  878.      * the geometry and setting the background from a 3-D border.
  879.      */
  880.  
  881.     if ((entryPtr->state != tkNormalUid)
  882.         && (entryPtr->state != tkDisabledUid)) {
  883.     Tcl_AppendResult(interp, "bad state value \"", entryPtr->state,
  884.         "\":  must be normal or disabled", (char *) NULL);
  885.     entryPtr->state = tkNormalUid;
  886.     return TCL_ERROR;
  887.     }
  888.  
  889.     Tk_SetBackgroundFromBorder(entryPtr->tkwin, entryPtr->normalBorder);
  890.  
  891.     gcValues.foreground = entryPtr->fgColorPtr->pixel;
  892.     gcValues.font = entryPtr->fontPtr->fid;
  893.     gcValues.graphics_exposures = False;
  894.     new = Tk_GetGC(entryPtr->tkwin, GCForeground|GCFont|GCGraphicsExposures,
  895.         &gcValues);
  896.     if (entryPtr->textGC != None) {
  897.     Tk_FreeGC(entryPtr->display, entryPtr->textGC);
  898.     }
  899.     entryPtr->textGC = new;
  900.  
  901.     gcValues.foreground = entryPtr->selFgColorPtr->pixel;
  902.     gcValues.font = entryPtr->fontPtr->fid;
  903.     new = Tk_GetGC(entryPtr->tkwin, GCForeground|GCFont, &gcValues);
  904.     if (entryPtr->selTextGC != None) {
  905.     Tk_FreeGC(entryPtr->display, entryPtr->selTextGC);
  906.     }
  907.     entryPtr->selTextGC = new;
  908.  
  909.     if (entryPtr->insertWidth <= 0) {
  910.     entryPtr->insertWidth = 2;
  911.     }
  912.     if (entryPtr->insertBorderWidth > entryPtr->insertWidth/2) {
  913.     entryPtr->insertBorderWidth = entryPtr->insertWidth/2;
  914.     }
  915.  
  916.     /*
  917.      * Restart the cursor timing sequence in case the on-time or off-time
  918.      * just changed.
  919.      */
  920.  
  921.     if (entryPtr->flags & GOT_FOCUS) {
  922.     EntryFocusProc(entryPtr, 1);
  923.     }
  924.  
  925.     /*
  926.      * Claim the selection if we've suddenly started exporting it.
  927.      */
  928.  
  929.     if (entryPtr->exportSelection && (!oldExport)
  930.         && (entryPtr->selectFirst != -1)) {
  931.     Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection,
  932.         (ClientData) entryPtr);
  933.     }
  934.  
  935.     /*
  936.      * Recompute the window's geometry and arrange for it to be
  937.      * redisplayed.
  938.      */
  939.  
  940.     Tk_SetInternalBorder(entryPtr->tkwin,
  941.         entryPtr->borderWidth + entryPtr->highlightWidth);
  942.     if (entryPtr->highlightWidth <= 0) {
  943.     entryPtr->highlightWidth = 0;
  944.     }
  945.     entryPtr->inset = entryPtr->highlightWidth + entryPtr->borderWidth + XPAD;
  946.     entryPtr->avgWidth = XTextWidth(entryPtr->fontPtr, "0", 1);
  947.     EntryComputeGeometry(entryPtr);
  948.     entryPtr->flags |= UPDATE_SCROLLBAR;
  949.     EventuallyRedraw(entryPtr);
  950.     return TCL_OK;
  951. }
  952.  
  953. /*
  954.  *--------------------------------------------------------------
  955.  *
  956.  * DisplayEntry --
  957.  *
  958.  *    This procedure redraws the contents of an entry window.
  959.  *
  960.  * Results:
  961.  *    None.
  962.  *
  963.  * Side effects:
  964.  *    Information appears on the screen.
  965.  *
  966.  *--------------------------------------------------------------
  967.  */
  968.  
  969. static void
  970. DisplayEntry(clientData)
  971.     ClientData clientData;    /* Information about window. */
  972. {
  973.     register Entry *entryPtr = (Entry *) clientData;
  974.     register Tk_Window tkwin = entryPtr->tkwin;
  975.     int baseY, selStartX, selEndX, index, cursorX;
  976.     int xBound, count;
  977.     Pixmap pixmap;
  978.     char *displayString;
  979.  
  980.     entryPtr->flags &= ~REDRAW_PENDING;
  981.     if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  982.     return;
  983.     }
  984.  
  985.     /*
  986.      * Update the scrollbar if that's needed.
  987.      */
  988.  
  989.     if (entryPtr->flags & UPDATE_SCROLLBAR) {
  990.     EntryUpdateScrollbar(entryPtr);
  991.     }
  992.  
  993.     /*
  994.      * In order to avoid screen flashes, this procedure redraws the
  995.      * textual area of the entry into off-screen memory, then copies
  996.      * it back on-screen in a single operation.  This means there's
  997.      * no point in time where the on-screen image has been cleared.
  998.      */
  999.  
  1000.     pixmap = Tk_GetPixmap(entryPtr->display, Tk_WindowId(tkwin),
  1001.         Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  1002.  
  1003.     /*
  1004.      * Compute x-coordinate of the pixel just after last visible
  1005.      * one, plus vertical position of baseline of text.
  1006.      */
  1007.  
  1008.     xBound = Tk_Width(tkwin) - entryPtr->inset;
  1009.     baseY = (Tk_Height(tkwin) + entryPtr->fontPtr->ascent
  1010.         - entryPtr->fontPtr->descent)/2;
  1011.  
  1012.     /*
  1013.      * Draw the background in three layers.  From bottom to top the
  1014.      * layers are:  normal background, selection background, and
  1015.      * insertion cursor background.
  1016.      */
  1017.  
  1018.     Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
  1019.         0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  1020.     if (entryPtr->displayString == NULL) {
  1021.     displayString = entryPtr->string;
  1022.     } else {
  1023.     displayString = entryPtr->displayString;
  1024.     }
  1025.     if (entryPtr->selectLast > entryPtr->leftIndex) {
  1026.     if (entryPtr->selectFirst <= entryPtr->leftIndex) {
  1027.         selStartX = entryPtr->leftX;
  1028.         index = entryPtr->leftIndex;
  1029.     } else {
  1030.         (void) TkMeasureChars(entryPtr->fontPtr,
  1031.             displayString + entryPtr->leftIndex,
  1032.             entryPtr->selectFirst - entryPtr->leftIndex,
  1033.             entryPtr->leftX, xBound, entryPtr->tabOrigin,
  1034.             TK_PARTIAL_OK|TK_NEWLINES_NOT_SPECIAL, &selStartX);
  1035.         index = entryPtr->selectFirst;
  1036.     }
  1037.     if ((selStartX - entryPtr->selBorderWidth) < xBound) {
  1038.         (void) TkMeasureChars(entryPtr->fontPtr,
  1039.             displayString + index, entryPtr->selectLast - index,
  1040.             selStartX, xBound, entryPtr->tabOrigin,
  1041.             TK_PARTIAL_OK|TK_NEWLINES_NOT_SPECIAL, &selEndX);
  1042.         Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->selBorder,
  1043.             selStartX - entryPtr->selBorderWidth,
  1044.             baseY - entryPtr->fontPtr->ascent
  1045.                 - entryPtr->selBorderWidth,
  1046.             (selEndX - selStartX) + 2*entryPtr->selBorderWidth,
  1047.             entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent
  1048.                 + 2*entryPtr->selBorderWidth,
  1049.             entryPtr->selBorderWidth, TK_RELIEF_RAISED);
  1050.     } else {
  1051.         selEndX = xBound;
  1052.     }
  1053.     }
  1054.  
  1055.     /*
  1056.      * Draw a special background for the insertion cursor, overriding
  1057.      * even the selection background.  As a special hack to keep the
  1058.      * cursor visible when the insertion cursor color is the same as
  1059.      * the color for selected text (e.g., on mono displays), write
  1060.      * background in the cursor area (instead of nothing) when the
  1061.      * cursor isn't on.  Otherwise the selection would hide the cursor.
  1062.      */
  1063.  
  1064.     if ((entryPtr->insertPos >= entryPtr->leftIndex)
  1065.         && (entryPtr->state == tkNormalUid)
  1066.         && (entryPtr->flags & GOT_FOCUS)) {
  1067.     (void) TkMeasureChars(entryPtr->fontPtr,
  1068.         displayString + entryPtr->leftIndex,
  1069.         entryPtr->insertPos - entryPtr->leftIndex, entryPtr->leftX,
  1070.         xBound + entryPtr->insertWidth, entryPtr->tabOrigin,
  1071.         TK_PARTIAL_OK|TK_NEWLINES_NOT_SPECIAL, &cursorX);
  1072.     cursorX -= (entryPtr->insertWidth)/2;
  1073.     if (cursorX < xBound) {
  1074.         if (entryPtr->flags & CURSOR_ON) {
  1075.         Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->insertBorder,
  1076.             cursorX, baseY - entryPtr->fontPtr->ascent,
  1077.             entryPtr->insertWidth,
  1078.             entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent,
  1079.             entryPtr->insertBorderWidth, TK_RELIEF_RAISED);
  1080.         } else if (entryPtr->insertBorder == entryPtr->selBorder) {
  1081.         Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
  1082.             cursorX, baseY - entryPtr->fontPtr->ascent,
  1083.             entryPtr->insertWidth,
  1084.             entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent,
  1085.             0, TK_RELIEF_FLAT);
  1086.         }
  1087.     }
  1088.     }
  1089.  
  1090.     /*
  1091.      * Draw the text in three pieces:  first the piece to the left of
  1092.      * the selection, then the selection, then the piece to the right
  1093.      * of the selection.
  1094.      */
  1095.  
  1096.     if (entryPtr->selectLast <= entryPtr->leftIndex) {
  1097.     TkDisplayChars(entryPtr->display, pixmap, entryPtr->textGC,
  1098.         entryPtr->fontPtr, displayString + entryPtr->leftIndex,
  1099.         entryPtr->numChars - entryPtr->leftIndex, entryPtr->leftX,
  1100.         baseY, entryPtr->tabOrigin, TK_NEWLINES_NOT_SPECIAL);
  1101.     } else {
  1102.     count = entryPtr->selectFirst - entryPtr->leftIndex;
  1103.     if (count > 0) {
  1104.         TkDisplayChars(entryPtr->display, pixmap, entryPtr->textGC,
  1105.             entryPtr->fontPtr, displayString + entryPtr->leftIndex,
  1106.             count, entryPtr->leftX, baseY, entryPtr->tabOrigin,
  1107.             TK_NEWLINES_NOT_SPECIAL);
  1108.         index = entryPtr->selectFirst;
  1109.     } else {
  1110.         index = entryPtr->leftIndex;
  1111.     }
  1112.     count = entryPtr->selectLast - index;
  1113.     if ((selStartX < xBound) && (count > 0)) {
  1114.         TkDisplayChars(entryPtr->display, pixmap, entryPtr->selTextGC,
  1115.             entryPtr->fontPtr, displayString + index, count,
  1116.             selStartX, baseY, entryPtr->tabOrigin,
  1117.             TK_NEWLINES_NOT_SPECIAL);
  1118.     }
  1119.     count = entryPtr->numChars - entryPtr->selectLast;
  1120.     if ((selEndX < xBound) && (count > 0)) {
  1121.         TkDisplayChars(entryPtr->display, pixmap, entryPtr->textGC,
  1122.             entryPtr->fontPtr,
  1123.             displayString + entryPtr->selectLast,
  1124.             count, selEndX, baseY, entryPtr->tabOrigin,
  1125.             TK_NEWLINES_NOT_SPECIAL);
  1126.     }
  1127.     }
  1128.  
  1129.     /*
  1130.      * Draw the border and focus highlight last, so they will overwrite
  1131.      * any text that extends past the viewable part of the window.
  1132.      */
  1133.  
  1134.     if (entryPtr->relief != TK_RELIEF_FLAT) {
  1135.     Tk_Draw3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
  1136.         entryPtr->highlightWidth, entryPtr->highlightWidth,
  1137.         Tk_Width(tkwin) - 2*entryPtr->highlightWidth,
  1138.         Tk_Height(tkwin) - 2*entryPtr->highlightWidth,
  1139.         entryPtr->borderWidth, entryPtr->relief);
  1140.     }
  1141.     if (entryPtr->highlightWidth != 0) {
  1142.     GC gc;
  1143.  
  1144.     if (entryPtr->flags & GOT_FOCUS) {
  1145.         gc = Tk_GCForColor(entryPtr->highlightColorPtr, pixmap);
  1146.     } else {
  1147.         gc = Tk_GCForColor(entryPtr->highlightBgColorPtr, pixmap);
  1148.     }
  1149.     Tk_DrawFocusHighlight(tkwin, gc, entryPtr->highlightWidth, pixmap);
  1150.     }
  1151.  
  1152.     /*
  1153.      * Everything's been redisplayed;  now copy the pixmap onto the screen
  1154.      * and free up the pixmap.
  1155.      */
  1156.  
  1157.     XCopyArea(entryPtr->display, pixmap, Tk_WindowId(tkwin), entryPtr->textGC,
  1158.         0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin),
  1159.         0, 0);
  1160.     Tk_FreePixmap(entryPtr->display, pixmap);
  1161.     entryPtr->flags &= ~BORDER_NEEDED;
  1162. }
  1163.  
  1164. /*
  1165.  *----------------------------------------------------------------------
  1166.  *
  1167.  * EntryComputeGeometry --
  1168.  *
  1169.  *    This procedure is invoked to recompute information about where
  1170.  *    in its window an entry's string will be displayed.  It also
  1171.  *    computes the requested size for the window.
  1172.  *
  1173.  * Results:
  1174.  *    None.
  1175.  *
  1176.  * Side effects:
  1177.  *    The leftX and tabOrigin fields are recomputed for entryPtr,
  1178.  *    and leftIndex may be adjusted.  Tk_GeometryRequest is called
  1179.  *    to register the desired dimensions for the window.
  1180.  *
  1181.  *----------------------------------------------------------------------
  1182.  */
  1183.  
  1184. static void
  1185. EntryComputeGeometry(entryPtr)
  1186.     Entry *entryPtr;            /* Widget record for entry. */
  1187. {
  1188.     int totalLength, overflow, maxOffScreen, rightX;
  1189.     int fontHeight, height, width, i;
  1190.     char *p, *displayString;
  1191.  
  1192.     /*
  1193.      * If we're displaying a special character instead of the value of
  1194.      * the entry, recompute the displayString.
  1195.      */
  1196.  
  1197.     if (entryPtr->displayString != NULL) {
  1198.     ckfree(entryPtr->displayString);
  1199.     entryPtr->displayString = NULL;
  1200.     }
  1201.     if (entryPtr->showChar != NULL) {
  1202.     entryPtr->displayString = (char *) ckalloc((unsigned)
  1203.         (entryPtr->numChars + 1));
  1204.     for (p = entryPtr->displayString, i = entryPtr->numChars; i > 0;
  1205.         i--, p++) {
  1206.         *p = entryPtr->showChar[0];
  1207.     }
  1208.     *p = 0;
  1209.     displayString = entryPtr->displayString;
  1210.     } else {
  1211.     displayString = entryPtr->string;
  1212.     }
  1213.  
  1214.     /*
  1215.      * Recompute where the leftmost character on the display will
  1216.      * be drawn (entryPtr->leftX) and adjust leftIndex if necessary
  1217.      * so that we don't let characters hang off the edge of the
  1218.      * window unless the entire window is full.
  1219.      */
  1220.  
  1221.     TkMeasureChars(entryPtr->fontPtr, displayString, entryPtr->numChars,
  1222.         0, INT_MAX, 0, TK_NEWLINES_NOT_SPECIAL, &totalLength);
  1223.     overflow = totalLength - (Tk_Width(entryPtr->tkwin) - 2*entryPtr->inset);
  1224.     if (overflow <= 0) {
  1225.     entryPtr->leftIndex = 0;
  1226.     if (entryPtr->justify == TK_JUSTIFY_LEFT) {
  1227.         entryPtr->leftX = entryPtr->inset;
  1228.     } else if (entryPtr->justify == TK_JUSTIFY_RIGHT) {
  1229.         entryPtr->leftX = Tk_Width(entryPtr->tkwin) - entryPtr->inset
  1230.             - totalLength;
  1231.     } else {
  1232.         entryPtr->leftX = (Tk_Width(entryPtr->tkwin) - totalLength)/2;
  1233.     }
  1234.     entryPtr->tabOrigin = entryPtr->leftX;
  1235.     } else {
  1236.     /*
  1237.      * The whole string can't fit in the window.  Compute the
  1238.      * maximum number of characters that may be off-screen to
  1239.      * the left without leaving empty space on the right of the
  1240.      * window, then don't let leftIndex be any greater than that.
  1241.      */
  1242.  
  1243.     maxOffScreen = TkMeasureChars(entryPtr->fontPtr, displayString,
  1244.         entryPtr->numChars, 0, overflow, 0,
  1245.         TK_NEWLINES_NOT_SPECIAL|TK_PARTIAL_OK, &rightX);
  1246.     if (rightX < overflow) {
  1247.         maxOffScreen += 1;
  1248.     }
  1249.     if (entryPtr->leftIndex > maxOffScreen) {
  1250.         entryPtr->leftIndex = maxOffScreen;
  1251.     }
  1252.     TkMeasureChars(entryPtr->fontPtr, displayString,
  1253.         entryPtr->leftIndex, 0, INT_MAX, 0,
  1254.         TK_NEWLINES_NOT_SPECIAL|TK_PARTIAL_OK, &rightX);
  1255.     entryPtr->leftX = entryPtr->inset;
  1256.     entryPtr->tabOrigin = entryPtr->leftX - rightX;
  1257.     }
  1258.  
  1259.     fontHeight = entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent;
  1260.     height = fontHeight + 2*entryPtr->inset + 2*(YPAD-XPAD);
  1261.     if (entryPtr->prefWidth > 0) {
  1262.     width = entryPtr->prefWidth*entryPtr->avgWidth + 2*entryPtr->inset;
  1263.     } else {
  1264.     if (totalLength == 0) {
  1265.         width = entryPtr->avgWidth + 2*entryPtr->inset;
  1266.     } else {
  1267.         width = totalLength + 2*entryPtr->inset;
  1268.     }
  1269.     }
  1270.     Tk_GeometryRequest(entryPtr->tkwin, width, height);
  1271. }
  1272.  
  1273. /*
  1274.  *----------------------------------------------------------------------
  1275.  *
  1276.  * InsertChars --
  1277.  *
  1278.  *    Add new characters to an entry widget.
  1279.  *
  1280.  * Results:
  1281.  *    None.
  1282.  *
  1283.  * Side effects:
  1284.  *    New information gets added to entryPtr;  it will be redisplayed
  1285.  *    soon, but not necessarily immediately.
  1286.  *
  1287.  *----------------------------------------------------------------------
  1288.  */
  1289.  
  1290. static void
  1291. InsertChars(entryPtr, index, string)
  1292.     register Entry *entryPtr;    /* Entry that is to get the new
  1293.                  * elements. */
  1294.     int index;            /* Add the new elements before this
  1295.                  * element. */
  1296.     char *string;        /* New characters to add (NULL-terminated
  1297.                  * string). */
  1298. {
  1299.     int length;
  1300.     char *new;
  1301.  
  1302.     length = strlen(string);
  1303.     if (length == 0) {
  1304.     return;
  1305.     }
  1306.     new = (char *) ckalloc((unsigned) (entryPtr->numChars + length + 1));
  1307.     strncpy(new, entryPtr->string, (size_t) index);
  1308.     strcpy(new+index, string);
  1309.     strcpy(new+index+length, entryPtr->string+index);
  1310.     ckfree(entryPtr->string);
  1311.     entryPtr->string = new;
  1312.     entryPtr->numChars += length;
  1313.  
  1314.     /*
  1315.      * Inserting characters invalidates all indexes into the string.
  1316.      * Touch up the indexes so that they still refer to the same
  1317.      * characters (at new positions).  When updating the selection
  1318.      * end-points, don't include the new text in the selection unless
  1319.      * it was completely surrounded by the selection.
  1320.      */
  1321.  
  1322.     if (entryPtr->selectFirst >= index) {
  1323.     entryPtr->selectFirst += length;
  1324.     }
  1325.     if (entryPtr->selectLast > index) {
  1326.     entryPtr->selectLast += length;
  1327.     }
  1328.     if ((entryPtr->selectAnchor > index) || (entryPtr->selectFirst >= index)) {
  1329.     entryPtr->selectAnchor += length;
  1330.     }
  1331.     if (entryPtr->leftIndex > index) {
  1332.     entryPtr->leftIndex += length;
  1333.     }
  1334.     if (entryPtr->insertPos >= index) {
  1335.     entryPtr->insertPos += length;
  1336.     }
  1337.  
  1338.     if (entryPtr->textVarName != NULL) {
  1339.     Tcl_SetVar(entryPtr->interp, entryPtr->textVarName, entryPtr->string,
  1340.         TCL_GLOBAL_ONLY);
  1341.     }
  1342.     entryPtr->flags |= UPDATE_SCROLLBAR;
  1343.     EntryComputeGeometry(entryPtr);
  1344.     EventuallyRedraw(entryPtr);
  1345. }
  1346.  
  1347. /*
  1348.  *----------------------------------------------------------------------
  1349.  *
  1350.  * DeleteChars --
  1351.  *
  1352.  *    Remove one or more characters from an entry widget.
  1353.  *
  1354.  * Results:
  1355.  *    None.
  1356.  *
  1357.  * Side effects:
  1358.  *    Memory gets freed, the entry gets modified and (eventually)
  1359.  *    redisplayed.
  1360.  *
  1361.  *----------------------------------------------------------------------
  1362.  */
  1363.  
  1364. static void
  1365. DeleteChars(entryPtr, index, count)
  1366.     register Entry *entryPtr;    /* Entry widget to modify. */
  1367.     int index;            /* Index of first character to delete. */
  1368.     int count;            /* How many characters to delete. */
  1369. {
  1370.     char *new;
  1371.  
  1372.     if ((index + count) > entryPtr->numChars) {
  1373.     count = entryPtr->numChars - index;
  1374.     }
  1375.     if (count <= 0) {
  1376.     return;
  1377.     }
  1378.  
  1379.     new = (char *) ckalloc((unsigned) (entryPtr->numChars + 1 - count));
  1380.     strncpy(new, entryPtr->string, (size_t) index);
  1381.     strcpy(new+index, entryPtr->string+index+count);
  1382.     ckfree(entryPtr->string);
  1383.     entryPtr->string = new;
  1384.     entryPtr->numChars -= count;
  1385.  
  1386.     /*
  1387.      * Deleting characters results in the remaining characters being
  1388.      * renumbered.  Update the various indexes into the string to reflect
  1389.      * this change.
  1390.      */
  1391.  
  1392.     if (entryPtr->selectFirst >= index) {
  1393.     if (entryPtr->selectFirst >= (index+count)) {
  1394.         entryPtr->selectFirst -= count;
  1395.     } else {
  1396.         entryPtr->selectFirst = index;
  1397.     }
  1398.     }
  1399.     if (entryPtr->selectLast >= index) {
  1400.     if (entryPtr->selectLast >= (index+count)) {
  1401.         entryPtr->selectLast -= count;
  1402.     } else {
  1403.         entryPtr->selectLast = index;
  1404.     }
  1405.     }
  1406.     if (entryPtr->selectLast <= entryPtr->selectFirst) {
  1407.     entryPtr->selectFirst = entryPtr->selectLast = -1;
  1408.     }
  1409.     if (entryPtr->selectAnchor >= index) {
  1410.     if (entryPtr->selectAnchor >= (index+count)) {
  1411.         entryPtr->selectAnchor -= count;
  1412.     } else {
  1413.         entryPtr->selectAnchor = index;
  1414.     }
  1415.     }
  1416.     if (entryPtr->leftIndex > index) {
  1417.     if (entryPtr->leftIndex >= (index+count)) {
  1418.         entryPtr->leftIndex -= count;
  1419.     } else {
  1420.         entryPtr->leftIndex = index;
  1421.     }
  1422.     }
  1423.     if (entryPtr->insertPos >= index) {
  1424.     if (entryPtr->insertPos >= (index+count)) {
  1425.         entryPtr->insertPos -= count;
  1426.     } else {
  1427.         entryPtr->insertPos = index;
  1428.     }
  1429.     }
  1430.  
  1431.     if (entryPtr->textVarName != NULL) {
  1432.     Tcl_SetVar(entryPtr->interp, entryPtr->textVarName, entryPtr->string,
  1433.         TCL_GLOBAL_ONLY);
  1434.     }
  1435.     entryPtr->flags |= UPDATE_SCROLLBAR;
  1436.     EntryComputeGeometry(entryPtr);
  1437.     EventuallyRedraw(entryPtr);
  1438. }
  1439.  
  1440. /*
  1441.  *----------------------------------------------------------------------
  1442.  *
  1443.  * EntrySetValue --
  1444.  *
  1445.  *    Replace the contents of a text entry with a given value.  This
  1446.  *    procedure is invoked when updating the entry from the entry's
  1447.  *    associated variable.
  1448.  *
  1449.  * Results:
  1450.  *    None.
  1451.  *
  1452.  * Side effects:
  1453.  *    The string displayed in the entry will change.  Any selection
  1454.  *    in the entry is lost and the insertion point gets set to the
  1455.  *    end of the entry.  Note: this procedure does *not* update the
  1456.  *    entry's associated variable, since that could result in an
  1457.  *    infinite loop.
  1458.  *
  1459.  *----------------------------------------------------------------------
  1460.  */
  1461.  
  1462. static void
  1463. EntrySetValue(entryPtr, value)
  1464.     register Entry *entryPtr;        /* Entry whose value is to be
  1465.                      * changed. */
  1466.     char *value;            /* New text to display in entry. */
  1467. {
  1468.     ckfree(entryPtr->string);
  1469.     entryPtr->numChars = strlen(value);
  1470.     entryPtr->string = (char *) ckalloc((unsigned) (entryPtr->numChars + 1));
  1471.     strcpy(entryPtr->string, value);
  1472.     entryPtr->selectFirst = entryPtr->selectLast = -1;
  1473.     entryPtr->leftIndex = 0;
  1474.     entryPtr->insertPos = entryPtr->numChars;
  1475.  
  1476.     entryPtr->flags |= UPDATE_SCROLLBAR;
  1477.     EntryComputeGeometry(entryPtr);
  1478.     EventuallyRedraw(entryPtr);
  1479. }
  1480.  
  1481. /*
  1482.  *--------------------------------------------------------------
  1483.  *
  1484.  * EntryEventProc --
  1485.  *
  1486.  *    This procedure is invoked by the Tk dispatcher for various
  1487.  *    events on entryes.
  1488.  *
  1489.  * Results:
  1490.  *    None.
  1491.  *
  1492.  * Side effects:
  1493.  *    When the window gets deleted, internal structures get
  1494.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1495.  *
  1496.  *--------------------------------------------------------------
  1497.  */
  1498.  
  1499. static void
  1500. EntryEventProc(clientData, eventPtr)
  1501.     ClientData clientData;    /* Information about window. */
  1502.     XEvent *eventPtr;        /* Information about event. */
  1503. {
  1504.     Entry *entryPtr = (Entry *) clientData;
  1505.     if (eventPtr->type == Expose) {
  1506.     EventuallyRedraw(entryPtr);
  1507.     entryPtr->flags |= BORDER_NEEDED;
  1508.     } else if (eventPtr->type == DestroyNotify) {
  1509.     if (entryPtr->tkwin != NULL) {
  1510.         entryPtr->tkwin = NULL;
  1511.         Tcl_DeleteCommand(entryPtr->interp,
  1512.             Tcl_GetCommandName(entryPtr->interp, entryPtr->widgetCmd));
  1513.     }
  1514.     if (entryPtr->flags & REDRAW_PENDING) {
  1515.         Tk_CancelIdleCall(DisplayEntry, (ClientData) entryPtr);
  1516.     }
  1517.     Tk_EventuallyFree((ClientData) entryPtr, DestroyEntry);
  1518.     } else if (eventPtr->type == ConfigureNotify) {
  1519.     Tk_Preserve((ClientData) entryPtr);
  1520.     entryPtr->flags |= UPDATE_SCROLLBAR;
  1521.     EntryComputeGeometry(entryPtr);
  1522.     EventuallyRedraw(entryPtr);
  1523.     Tk_Release((ClientData) entryPtr);
  1524.     } else if (eventPtr->type == FocusIn) {
  1525.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1526.         EntryFocusProc(entryPtr, 1);
  1527.     }
  1528.     } else if (eventPtr->type == FocusOut) {
  1529.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1530.         EntryFocusProc(entryPtr, 0);
  1531.     }
  1532.     }
  1533. }
  1534.  
  1535. /*
  1536.  *----------------------------------------------------------------------
  1537.  *
  1538.  * EntryCmdDeletedProc --
  1539.  *
  1540.  *    This procedure is invoked when a widget command is deleted.  If
  1541.  *    the widget isn't already in the process of being destroyed,
  1542.  *    this command destroys it.
  1543.  *
  1544.  * Results:
  1545.  *    None.
  1546.  *
  1547.  * Side effects:
  1548.  *    The widget is destroyed.
  1549.  *
  1550.  *----------------------------------------------------------------------
  1551.  */
  1552.  
  1553. static void
  1554. EntryCmdDeletedProc(clientData)
  1555.     ClientData clientData;    /* Pointer to widget record for widget. */
  1556. {
  1557.     Entry *entryPtr = (Entry *) clientData;
  1558.     Tk_Window tkwin = entryPtr->tkwin;
  1559.  
  1560.     /*
  1561.      * This procedure could be invoked either because the window was
  1562.      * destroyed and the command was then deleted (in which case tkwin
  1563.      * is NULL) or because the command was deleted, and then this procedure
  1564.      * destroys the widget.
  1565.      */
  1566.  
  1567.     if (tkwin != NULL) {
  1568.     entryPtr->tkwin = NULL;
  1569.     Tk_DestroyWindow(tkwin);
  1570.     }
  1571. }
  1572.  
  1573. /*
  1574.  *--------------------------------------------------------------
  1575.  *
  1576.  * GetEntryIndex --
  1577.  *
  1578.  *    Parse an index into an entry and return either its value
  1579.  *    or an error.
  1580.  *
  1581.  * Results:
  1582.  *    A standard Tcl result.  If all went well, then *indexPtr is
  1583.  *    filled in with the index (into entryPtr) corresponding to
  1584.  *    string.  The index value is guaranteed to lie between 0 and
  1585.  *    the number of characters in the string, inclusive.  If an
  1586.  *    error occurs then an error message is left in interp->result.
  1587.  *
  1588.  * Side effects:
  1589.  *    None.
  1590.  *
  1591.  *--------------------------------------------------------------
  1592.  */
  1593.  
  1594. static int
  1595. GetEntryIndex(interp, entryPtr, string, indexPtr)
  1596.     Tcl_Interp *interp;        /* For error messages. */
  1597.     Entry *entryPtr;        /* Entry for which the index is being
  1598.                  * specified. */
  1599.     char *string;        /* Specifies character in entryPtr. */
  1600.     int *indexPtr;        /* Where to store converted index. */
  1601. {
  1602.     size_t length;
  1603.  
  1604.     length = strlen(string);
  1605.  
  1606.     if (string[0] == 'a') {
  1607.     if (strncmp(string, "anchor", length) == 0) {
  1608.         *indexPtr = entryPtr->selectAnchor;
  1609.     } else {
  1610.         badIndex:
  1611.  
  1612.         /*
  1613.          * Some of the paths here leave messages in interp->result,
  1614.          * so we have to clear it out before storing our own message.
  1615.          */
  1616.  
  1617.         Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
  1618.         Tcl_AppendResult(interp, "bad entry index \"", string,
  1619.             "\"", (char *) NULL);
  1620.         return TCL_ERROR;
  1621.     }
  1622.     } else if (string[0] == 'e') {
  1623.     if (strncmp(string, "end", length) == 0) {
  1624.         *indexPtr = entryPtr->numChars;
  1625.     } else {
  1626.         goto badIndex;
  1627.     }
  1628.     } else if (string[0] == 'i') {
  1629.     if (strncmp(string, "insert", length) == 0) {
  1630.         *indexPtr = entryPtr->insertPos;
  1631.     } else {
  1632.         goto badIndex;
  1633.     }
  1634.     } else if (string[0] == 's') {
  1635.     if (entryPtr->selectFirst == -1) {
  1636.         interp->result = "selection isn't in entry";
  1637.         return TCL_ERROR;
  1638.     }
  1639.     if (length < 5) {
  1640.         goto badIndex;
  1641.     }
  1642.     if (strncmp(string, "sel.first", length) == 0) {
  1643.         *indexPtr = entryPtr->selectFirst;
  1644.     } else if (strncmp(string, "sel.last", length) == 0) {
  1645.         *indexPtr = entryPtr->selectLast;
  1646.     } else {
  1647.         goto badIndex;
  1648.     }
  1649.     } else if (string[0] == '@') {
  1650.     int x, dummy, roundUp;
  1651.  
  1652.     if (Tcl_GetInt(interp, string+1, &x) != TCL_OK) {
  1653.         goto badIndex;
  1654.     }
  1655.     if (x < entryPtr->inset) {
  1656.         x = entryPtr->inset;
  1657.     }
  1658.     roundUp = 0;
  1659.     if (x >= (Tk_Width(entryPtr->tkwin) - entryPtr->inset)) {
  1660.         x = Tk_Width(entryPtr->tkwin) - entryPtr->inset - 1;
  1661.         roundUp = 1;
  1662.     }
  1663.     if (entryPtr->numChars == 0) {
  1664.         *indexPtr = 0;
  1665.     } else {
  1666.         *indexPtr = TkMeasureChars(entryPtr->fontPtr,
  1667.             (entryPtr->displayString == NULL) ? entryPtr->string
  1668.             : entryPtr->displayString,
  1669.             entryPtr->numChars, entryPtr->tabOrigin, x,
  1670.             entryPtr->tabOrigin, TK_NEWLINES_NOT_SPECIAL, &dummy);
  1671.     }
  1672.  
  1673.     /*
  1674.      * Special trick:  if the x-position was off-screen to the right,
  1675.      * round the index up to refer to the character just after the
  1676.      * last visible one on the screen.  This is needed to enable the
  1677.      * last character to be selected, for example.
  1678.      */
  1679.  
  1680.     if (roundUp && (*indexPtr < entryPtr->numChars)) {
  1681.         *indexPtr += 1;
  1682.     }
  1683.     } else {
  1684.     if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
  1685.         goto badIndex;
  1686.     }
  1687.     if (*indexPtr < 0){
  1688.         *indexPtr = 0;
  1689.     } else if (*indexPtr > entryPtr->numChars) {
  1690.         *indexPtr = entryPtr->numChars;
  1691.     }
  1692.     }
  1693.     return TCL_OK;
  1694. }
  1695.  
  1696. /*
  1697.  *----------------------------------------------------------------------
  1698.  *
  1699.  * EntryScanTo --
  1700.  *
  1701.  *    Given a y-coordinate (presumably of the curent mouse location)
  1702.  *    drag the view in the window to implement the scan operation.
  1703.  *
  1704.  * Results:
  1705.  *    None.
  1706.  *
  1707.  * Side effects:
  1708.  *    The view in the window may change.
  1709.  *
  1710.  *----------------------------------------------------------------------
  1711.  */
  1712.  
  1713. static void
  1714. EntryScanTo(entryPtr, x)
  1715.     register Entry *entryPtr;        /* Information about widget. */
  1716.     int x;                /* X-coordinate to use for scan
  1717.                      * operation. */
  1718. {
  1719.     int newLeftIndex;
  1720.  
  1721.     /*
  1722.      * Compute new leftIndex for entry by amplifying the difference
  1723.      * between the current position and the place where the scan
  1724.      * started (the "mark" position).  If we run off the left or right
  1725.      * side of the entry, then reset the mark point so that the current
  1726.      * position continues to correspond to the edge of the window.
  1727.      * This means that the picture will start dragging as soon as the
  1728.      * mouse reverses direction (without this reset, might have to slide
  1729.      * mouse a long ways back before the picture starts moving again).
  1730.      */
  1731.  
  1732.     newLeftIndex = entryPtr->scanMarkIndex
  1733.         - (10*(x - entryPtr->scanMarkX))/entryPtr->avgWidth;
  1734.     if (newLeftIndex >= entryPtr->numChars) {
  1735.     newLeftIndex = entryPtr->scanMarkIndex = entryPtr->numChars-1;
  1736.     entryPtr->scanMarkX = x;
  1737.     }
  1738.     if (newLeftIndex < 0) {
  1739.     newLeftIndex = entryPtr->scanMarkIndex = 0;
  1740.     entryPtr->scanMarkX = x;
  1741.     } 
  1742.     if (newLeftIndex != entryPtr->leftIndex) {
  1743.     entryPtr->leftIndex = newLeftIndex;
  1744.     entryPtr->flags |= UPDATE_SCROLLBAR;
  1745.     EntryComputeGeometry(entryPtr);
  1746.     EventuallyRedraw(entryPtr);
  1747.     }
  1748. }
  1749.  
  1750. /*
  1751.  *----------------------------------------------------------------------
  1752.  *
  1753.  * EntrySelectTo --
  1754.  *
  1755.  *    Modify the selection by moving its un-anchored end.  This could
  1756.  *    make the selection either larger or smaller.
  1757.  *
  1758.  * Results:
  1759.  *    None.
  1760.  *
  1761.  * Side effects:
  1762.  *    The selection changes.
  1763.  *
  1764.  *----------------------------------------------------------------------
  1765.  */
  1766.  
  1767. static void
  1768. EntrySelectTo(entryPtr, index)
  1769.     register Entry *entryPtr;        /* Information about widget. */
  1770.     int index;                /* Index of element that is to
  1771.                      * become the "other" end of the
  1772.                      * selection. */
  1773. {
  1774.     int newFirst, newLast;
  1775.  
  1776.     /*
  1777.      * Grab the selection if we don't own it already.
  1778.      */
  1779.  
  1780.     if ((entryPtr->selectFirst == -1) && (entryPtr->exportSelection)) {
  1781.     Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection,
  1782.         (ClientData) entryPtr);
  1783.     }
  1784.  
  1785.     /*
  1786.      * Pick new starting and ending points for the selection.
  1787.      */
  1788.  
  1789.     if (entryPtr->selectAnchor > entryPtr->numChars) {
  1790.     entryPtr->selectAnchor = entryPtr->numChars;
  1791.     }
  1792.     if (entryPtr->selectAnchor <= index) {
  1793.     newFirst = entryPtr->selectAnchor;
  1794.     newLast = index;
  1795.     } else {
  1796.     newFirst = index;
  1797.     newLast = entryPtr->selectAnchor;
  1798.     if (newLast < 0) {
  1799.         newFirst = newLast = -1;
  1800.     }
  1801.     }
  1802.     if ((entryPtr->selectFirst == newFirst)
  1803.         && (entryPtr->selectLast == newLast)) {
  1804.     return;
  1805.     }
  1806.     entryPtr->selectFirst = newFirst;
  1807.     entryPtr->selectLast = newLast;
  1808.     EventuallyRedraw(entryPtr);
  1809. }
  1810.  
  1811. /*
  1812.  *----------------------------------------------------------------------
  1813.  *
  1814.  * EntryFetchSelection --
  1815.  *
  1816.  *    This procedure is called back by Tk when the selection is
  1817.  *    requested by someone.  It returns part or all of the selection
  1818.  *    in a buffer provided by the caller.
  1819.  *
  1820.  * Results:
  1821.  *    The return value is the number of non-NULL bytes stored
  1822.  *    at buffer.  Buffer is filled (or partially filled) with a
  1823.  *    NULL-terminated string containing part or all of the selection,
  1824.  *    as given by offset and maxBytes.
  1825.  *
  1826.  * Side effects:
  1827.  *    None.
  1828.  *
  1829.  *----------------------------------------------------------------------
  1830.  */
  1831.  
  1832. static int
  1833. EntryFetchSelection(clientData, offset, buffer, maxBytes)
  1834.     ClientData clientData;        /* Information about entry widget. */
  1835.     int offset;                /* Offset within selection of first
  1836.                      * character to be returned. */
  1837.     char *buffer;            /* Location in which to place
  1838.                      * selection. */
  1839.     int maxBytes;            /* Maximum number of bytes to place
  1840.                      * at buffer, not including terminating
  1841.                      * NULL character. */
  1842. {
  1843.     Entry *entryPtr = (Entry *) clientData;
  1844.     int count;
  1845.     char *displayString;
  1846.  
  1847.     if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) {
  1848.     return -1;
  1849.     }
  1850.     count = entryPtr->selectLast - entryPtr->selectFirst - offset;
  1851.     if (count > maxBytes) {
  1852.     count = maxBytes;
  1853.     }
  1854.     if (count <= 0) {
  1855.     return 0;
  1856.     }
  1857.     if (entryPtr->displayString == NULL) {
  1858.     displayString = entryPtr->string;
  1859.     } else {
  1860.     displayString = entryPtr->displayString;
  1861.     }
  1862.     strncpy(buffer, displayString + entryPtr->selectFirst + offset,
  1863.         (size_t) count);
  1864.     buffer[count] = '\0';
  1865.     return count;
  1866. }
  1867.  
  1868. /*
  1869.  *----------------------------------------------------------------------
  1870.  *
  1871.  * EntryLostSelection --
  1872.  *
  1873.  *    This procedure is called back by Tk when the selection is
  1874.  *    grabbed away from an entry widget.
  1875.  *
  1876.  * Results:
  1877.  *    None.
  1878.  *
  1879.  * Side effects:
  1880.  *    The existing selection is unhighlighted, and the window is
  1881.  *    marked as not containing a selection.
  1882.  *
  1883.  *----------------------------------------------------------------------
  1884.  */
  1885.  
  1886. static void
  1887. EntryLostSelection(clientData)
  1888.     ClientData clientData;        /* Information about entry widget. */
  1889. {
  1890.     Entry *entryPtr = (Entry *) clientData;
  1891.  
  1892.     if ((entryPtr->selectFirst != -1) && entryPtr->exportSelection) {
  1893.     entryPtr->selectFirst = -1;
  1894.     entryPtr->selectLast = -1;
  1895.     EventuallyRedraw(entryPtr);
  1896.     }
  1897. }
  1898.  
  1899. /*
  1900.  *----------------------------------------------------------------------
  1901.  *
  1902.  * EventuallyRedraw --
  1903.  *
  1904.  *    Ensure that an entry is eventually redrawn on the display.
  1905.  *
  1906.  * Results:
  1907.  *    None.
  1908.  *
  1909.  * Side effects:
  1910.  *    Information gets redisplayed.  Right now we don't do selective
  1911.  *    redisplays:  the whole window will be redrawn.  This doesn't
  1912.  *    seem to hurt performance noticeably, but if it does then this
  1913.  *    could be changed.
  1914.  *
  1915.  *----------------------------------------------------------------------
  1916.  */
  1917.  
  1918. static void
  1919. EventuallyRedraw(entryPtr)
  1920.     register Entry *entryPtr;        /* Information about widget. */
  1921. {
  1922.     if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(entryPtr->tkwin)) {
  1923.     return;
  1924.     }
  1925.  
  1926.     /*
  1927.      * Right now we don't do selective redisplays:  the whole window
  1928.      * will be redrawn.  This doesn't seem to hurt performance noticeably,
  1929.      * but if it does then this could be changed.
  1930.      */
  1931.  
  1932.     if (!(entryPtr->flags & REDRAW_PENDING)) {
  1933.     entryPtr->flags |= REDRAW_PENDING;
  1934.     Tk_DoWhenIdle(DisplayEntry, (ClientData) entryPtr);
  1935.     }
  1936. }
  1937.  
  1938. /*
  1939.  *----------------------------------------------------------------------
  1940.  *
  1941.  * EntryVisibleRange --
  1942.  *
  1943.  *    Return information about the range of the entry that is
  1944.  *    currently visible.
  1945.  *
  1946.  * Results:
  1947.  *    *firstPtr and *lastPtr are modified to hold fractions between
  1948.  *    0 and 1 identifying the range of characters visible in the
  1949.  *    entry.
  1950.  *
  1951.  * Side effects:
  1952.  *    None.
  1953.  *
  1954.  *----------------------------------------------------------------------
  1955.  */
  1956.  
  1957. static void
  1958. EntryVisibleRange(entryPtr, firstPtr, lastPtr)
  1959.     Entry *entryPtr;            /* Information about widget. */
  1960.     double *firstPtr;            /* Return position of first visible
  1961.                      * character in widget. */
  1962.     double *lastPtr;            /* Return position of char just after
  1963.                      * last visible one. */
  1964. {
  1965.     char *displayString;
  1966.     int charsInWindow, endX;
  1967.  
  1968.     if (entryPtr->displayString == NULL) {
  1969.     displayString = entryPtr->string;
  1970.     } else {
  1971.     displayString = entryPtr->displayString;
  1972.     }
  1973.     if (entryPtr->numChars == 0) {
  1974.     *firstPtr = 0.0;
  1975.     *lastPtr = 1.0;
  1976.     } else {
  1977.     charsInWindow = TkMeasureChars(entryPtr->fontPtr,
  1978.         displayString + entryPtr->leftIndex,
  1979.         entryPtr->numChars - entryPtr->leftIndex, entryPtr->inset,
  1980.         Tk_Width(entryPtr->tkwin) - entryPtr->inset, entryPtr->inset,
  1981.         TK_AT_LEAST_ONE|TK_NEWLINES_NOT_SPECIAL, &endX);
  1982.     *firstPtr = ((double) entryPtr->leftIndex)/entryPtr->numChars;
  1983.     *lastPtr = ((double) (entryPtr->leftIndex + charsInWindow))
  1984.         /entryPtr->numChars;
  1985.     }
  1986. }
  1987.  
  1988. /*
  1989.  *----------------------------------------------------------------------
  1990.  *
  1991.  * EntryUpdateScrollbar --
  1992.  *
  1993.  *    This procedure is invoked whenever information has changed in
  1994.  *    an entry in a way that would invalidate a scrollbar display.
  1995.  *    If there is an associated scrollbar, then this procedure updates
  1996.  *    it by invoking a Tcl command.
  1997.  *
  1998.  * Results:
  1999.  *    None.
  2000.  *
  2001.  * Side effects:
  2002.  *    A Tcl command is invoked, and an additional command may be
  2003.  *    invoked to process errors in the command.
  2004.  *
  2005.  *----------------------------------------------------------------------
  2006.  */
  2007.  
  2008. static void
  2009. EntryUpdateScrollbar(entryPtr)
  2010.     Entry *entryPtr;            /* Information about widget. */
  2011. {
  2012.     char args[100];
  2013.     int code;
  2014.     double first, last;
  2015.  
  2016.     if (entryPtr->scrollCmd == NULL) {
  2017.     return;
  2018.     }
  2019.  
  2020.     EntryVisibleRange(entryPtr, &first, &last);
  2021.     sprintf(args, " %g %g", first, last);
  2022.     code = Tcl_VarEval(entryPtr->interp, entryPtr->scrollCmd, args,
  2023.         (char *) NULL);
  2024.     if (code != TCL_OK) {
  2025.     Tcl_AddErrorInfo(entryPtr->interp,
  2026.         "\n    (horizontal scrolling command executed by entry)");
  2027.     Tk_BackgroundError(entryPtr->interp);
  2028.     }
  2029.     Tcl_SetResult(entryPtr->interp, (char *) NULL, TCL_STATIC);
  2030. }
  2031.  
  2032. /*
  2033.  *----------------------------------------------------------------------
  2034.  *
  2035.  * EntryBlinkProc --
  2036.  *
  2037.  *    This procedure is called as a timer handler to blink the
  2038.  *    insertion cursor off and on.
  2039.  *
  2040.  * Results:
  2041.  *    None.
  2042.  *
  2043.  * Side effects:
  2044.  *    The cursor gets turned on or off, redisplay gets invoked,
  2045.  *    and this procedure reschedules itself.
  2046.  *
  2047.  *----------------------------------------------------------------------
  2048.  */
  2049.  
  2050. static void
  2051. EntryBlinkProc(clientData)
  2052.     ClientData clientData;    /* Pointer to record describing entry. */
  2053. {
  2054.     register Entry *entryPtr = (Entry *) clientData;
  2055.  
  2056.     if (!(entryPtr->flags & GOT_FOCUS) || (entryPtr->insertOffTime == 0)) {
  2057.     return;
  2058.     }
  2059.     if (entryPtr->flags & CURSOR_ON) {
  2060.     entryPtr->flags &= ~CURSOR_ON;
  2061.     entryPtr->insertBlinkHandler = Tk_CreateTimerHandler(
  2062.         entryPtr->insertOffTime, EntryBlinkProc, (ClientData) entryPtr);
  2063.     } else {
  2064.     entryPtr->flags |= CURSOR_ON;
  2065.     entryPtr->insertBlinkHandler = Tk_CreateTimerHandler(
  2066.         entryPtr->insertOnTime, EntryBlinkProc, (ClientData) entryPtr);
  2067.     }
  2068.     EventuallyRedraw(entryPtr);
  2069. }
  2070.  
  2071. /*
  2072.  *----------------------------------------------------------------------
  2073.  *
  2074.  * EntryFocusProc --
  2075.  *
  2076.  *    This procedure is called whenever the entry gets or loses the
  2077.  *    input focus.  It's also called whenever the window is reconfigured
  2078.  *    while it has the focus.
  2079.  *
  2080.  * Results:
  2081.  *    None.
  2082.  *
  2083.  * Side effects:
  2084.  *    The cursor gets turned on or off.
  2085.  *
  2086.  *----------------------------------------------------------------------
  2087.  */
  2088.  
  2089. static void
  2090. EntryFocusProc(entryPtr, gotFocus)
  2091.     register Entry *entryPtr;    /* Entry that got or lost focus. */
  2092.     int gotFocus;        /* 1 means window is getting focus, 0 means
  2093.                  * it's losing it. */
  2094. {
  2095.     Tk_DeleteTimerHandler(entryPtr->insertBlinkHandler);
  2096.     if (gotFocus) {
  2097.     entryPtr->flags |= GOT_FOCUS | CURSOR_ON;
  2098.     if (entryPtr->insertOffTime != 0) {
  2099.         entryPtr->insertBlinkHandler = Tk_CreateTimerHandler(
  2100.             entryPtr->insertOnTime, EntryBlinkProc,
  2101.             (ClientData) entryPtr);
  2102.     }
  2103.     } else {
  2104.     entryPtr->flags &= ~(GOT_FOCUS | CURSOR_ON);
  2105.     entryPtr->insertBlinkHandler = (Tk_TimerToken) NULL;
  2106.     }
  2107.     EventuallyRedraw(entryPtr);
  2108. }
  2109.  
  2110. /*
  2111.  *--------------------------------------------------------------
  2112.  *
  2113.  * EntryTextVarProc --
  2114.  *
  2115.  *    This procedure is invoked when someone changes the variable
  2116.  *    whose contents are to be displayed in an entry.
  2117.  *
  2118.  * Results:
  2119.  *    NULL is always returned.
  2120.  *
  2121.  * Side effects:
  2122.  *    The text displayed in the entry will change to match the
  2123.  *    variable.
  2124.  *
  2125.  *--------------------------------------------------------------
  2126.  */
  2127.  
  2128.     /* ARGSUSED */
  2129. static char *
  2130. EntryTextVarProc(clientData, interp, name1, name2, flags)
  2131.     ClientData clientData;    /* Information about button. */
  2132.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  2133.     char *name1;        /* Not used. */
  2134.     char *name2;        /* Not used. */
  2135.     int flags;            /* Information about what happened. */
  2136. {
  2137.     register Entry *entryPtr = (Entry *) clientData;
  2138.     char *value;
  2139.  
  2140.     /*
  2141.      * If the variable is unset, then immediately recreate it unless
  2142.      * the whole interpreter is going away.
  2143.      */
  2144.  
  2145.     if (flags & TCL_TRACE_UNSETS) {
  2146.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  2147.         Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
  2148.             TCL_GLOBAL_ONLY);
  2149.         Tcl_TraceVar(interp, entryPtr->textVarName,
  2150.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  2151.             EntryTextVarProc, clientData);
  2152.     }
  2153.     return (char *) NULL;
  2154.     }
  2155.  
  2156.     /*
  2157.      * Update the entry's text with the value of the variable, unless
  2158.      * the entry already has that value (this happens when the variable
  2159.      * changes value because we changed it because someone typed in
  2160.      * the entry).
  2161.      */
  2162.  
  2163.     value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
  2164.     if (value == NULL) {
  2165.     value = "";
  2166.     }
  2167.     if (strcmp(value, entryPtr->string) != 0) {
  2168.     EntrySetValue(entryPtr, value);
  2169.     }
  2170.     return (char *) NULL;
  2171. }
  2172.